pacenotes_installer\engine\execute/
install_package.rs1pub struct Progress {
3 pub written: u64,
4 pub total: u64,
5}
6
7pub struct Installing<'a> {
11 pub key: &'a str,
12 pub sha256: &'a str,
13 pub version: &'a str,
14 pub exclusive_group: Option<&'a str>,
15}
16
17pub fn install_package(
20 run: &mut super::run::RunContext,
21 what: &Installing,
22 files: &[crate::zip::FileEntry],
23 point: &str,
24 progress: &mut Progress,
25) -> Result<(), super::ExecuteError> {
26 let key = what.key;
27 let payload = std::fs::read(run.cache.payload_path(what.sha256))
28 .map_err(|e| super::ExecuteError::extract(key, e))?;
29 let archive =
30 crate::zip::Archive::open(&payload).map_err(|e| super::ExecuteError::extract(key, e))?;
31
32 let phase = crate::engine::Phase::BackingUp;
33 run.watcher
34 .phase(phase, key, progress.written, progress.total);
35 let mut entry = crate::ledger::LedgerEntry {
36 sha256: what.sha256.to_string(),
37 version: what.version.to_string(),
38 exclusive_group: what.exclusive_group.map(str::to_string),
39 state: crate::ledger::EntryState::Installing,
40 files: record_priors(run, files, point, &archive)?,
41 };
42 run.ledger
43 .put(key, entry.clone())
44 .map_err(super::ExecuteError::ledger)?;
45
46 let phase = crate::engine::Phase::Installing;
47 run.watcher
48 .phase(phase, key, progress.written, progress.total);
49 for file in files {
50 let recorded = entry.files.get_mut(&crate::ledger::fold(&file.path));
51 let identical = recorded
53 .as_ref()
54 .is_some_and(|recorded| recorded.prior == crate::ledger::Prior::Identical);
55 if !identical {
56 let bytes = archive
57 .read(&file.path)
58 .map_err(|e| super::ExecuteError::extract(key, e))?;
59 super::game_files::write_atomic(
60 &crate::relative_path::join(run.game_dir, &file.path),
61 &bytes,
62 )?;
63 if let Some(recorded) = recorded {
64 recorded.sha256 = crate::sha256::hex_digest(&bytes);
65 }
66 }
67 progress.written += file.size;
68 run.watcher
69 .phase(phase, &file.path, progress.written, progress.total);
70 }
71
72 entry.state = crate::ledger::EntryState::Installed;
73 run.ledger
74 .put(key, entry)
75 .map_err(super::ExecuteError::ledger)?;
76 run.watcher.log(&format!("installed {key}"));
77 Ok(())
78}
79
80fn record_priors(
83 run: &mut super::run::RunContext,
84 files: &[crate::zip::FileEntry],
85 point: &str,
86 archive: &crate::zip::Archive,
87) -> Result<std::collections::BTreeMap<String, crate::ledger::LedgerFile>, super::ExecuteError> {
88 let mut recorded = std::collections::BTreeMap::new();
89 for file in files {
90 let (prior, sha256) = preserve_prior(run, point, file, archive)?;
91 let ledger_file = crate::ledger::LedgerFile {
92 path: file.path.clone(),
93 sha256,
94 prior,
95 };
96 recorded.insert(crate::ledger::fold(&file.path), ledger_file);
97 }
98 Ok(recorded)
99}
100
101fn preserve_prior(
105 run: &mut super::run::RunContext,
106 point: &str,
107 file: &crate::zip::FileEntry,
108 archive: &crate::zip::Archive,
109) -> Result<(crate::ledger::Prior, String), super::ExecuteError> {
110 let target = crate::relative_path::join(run.game_dir, &file.path);
111 if !target.exists() || run.ledger.owns(&file.path) {
113 return Ok((crate::ledger::Prior::Absent, String::new()));
114 }
115 if let (Ok(existing), Ok(incoming)) = (std::fs::read(&target), archive.read(&file.path)) {
116 if existing == incoming {
117 let sha256 = crate::sha256::hex_digest(&incoming);
118 return Ok((crate::ledger::Prior::Identical, sha256));
119 }
120 }
121
122 let copy = super::restore_point::saved_copy(run.game_dir, point, &file.path);
123 super::game_files::copy_file(&target, ©)?;
124 run.watcher
125 .log(&format!("saved the existing {}", file.path));
126 let prior = crate::ledger::Prior::Backup {
127 point: point.to_string(),
128 };
129 Ok((prior, String::new()))
130}